package service

import (
	"context"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"gitee.com/fu-ce-wei/pi-mapper/dto"
	"gitee.com/fu-ce-wei/pi-mapper/internal/config"
	"gitee.com/fu-ce-wei/pi-mapper/pkg/logger"
	"gitee.com/fu-ce-wei/pi-mapper/pkg/redis"
	"go.uber.org/zap"
)

// ImageScanService 图片扫描服务接口
type ImageScanService interface {
	// ScanImageDirectory 扫描图片目录并将图片文件添加到Redis
	ScanImageDirectory(ctx context.Context) (*dto.ScanResult, error)

	// ScanImageFiles 扫描图片文件
	ScanImageFiles(ctx context.Context) error

	// GetTaskStatus 获取任务状态
	GetTaskStatus(ctx context.Context) (*dto.TaskStatus, error)

	// GetScanStats 获取扫描统计信息
	GetScanStats(ctx context.Context) (*ImageScanStats, error)

	// CancelScan 取消正在进行的扫描
	CancelScan()

	// ValidateDirectory 验证目录是否存在且可读
	ValidateDirectory() error

	// GetDirectoryStats 获取目录统计信息
	GetDirectoryStats() (map[string]interface{}, error)
}

// ImageScanServiceImpl 图片扫描服务实现
type ImageScanServiceImpl struct {
	redisClient *redis.Client
	config      *config.Config
	logger      *logger.Logger
	cancelFunc  context.CancelFunc
	mu          sync.Mutex
	isScanning  bool
}

// ImageScanStats 图片扫描统计信息
type ImageScanStats struct {
	LastScanTime    time.Time
	LastScanResult  *dto.ScanResult
	TotalScans      int64
	SuccessCount    int64
	FailureCount    int64
	AverageDuration time.Duration
}

// NewImageScanService 创建图片扫描服务实例
func NewImageScanService(redisClient *redis.Client, config *config.Config, logger *logger.Logger) ImageScanService {
	return &ImageScanServiceImpl{
		redisClient: redisClient,
		config:      config,
		logger:      logger,
	}
}

// ScanImageDirectory 扫描图片目录并将图片文件添加到Redis
func (s *ImageScanServiceImpl) ScanImageDirectory(ctx context.Context) (*dto.ScanResult, error) {
	s.mu.Lock()
	if s.isScanning {
		s.mu.Unlock()
		return nil, fmt.Errorf("图片扫描任务已在进行中")
	}
	s.isScanning = true
	s.mu.Unlock()

	defer func() {
		s.mu.Lock()
		s.isScanning = false
		s.mu.Unlock()
	}()

	// 创建可取消的上下文
	ctx, cancel := context.WithCancel(ctx)
	s.cancelFunc = cancel
	defer cancel()

	startTime := time.Now()

	s.logger.Info("开始扫描图片目录",
		zap.String("directory", s.config.Image.RootPath),
		zap.Int("max_files", s.config.Image.MaxFiles),
		zap.Int("workers", s.config.Image.Workers),
	)

	// 扫描文件
	files, err := s.scanImageFiles(ctx)
	if err != nil {
		s.logger.Error("扫描图片文件失败",
			zap.Error(err),
			zap.String("directory", s.config.Image.RootPath),
		)
		return nil, fmt.Errorf("扫描图片文件失败: %w", err)
	}

	// 如果没有找到文件
	if len(files) == 0 {
		s.logger.Warn("没有找到图片文件",
			zap.String("directory", s.config.Image.RootPath),
		)
		return &dto.ScanResult{
			TotalFiles:     0,
			ProcessedFiles: 0,
			AddedFiles:     0,
			SkippedFiles:   0,
			FailedFiles:    0,
			Duration:       time.Since(startTime),
			StartTime:      startTime,
			EndTime:        time.Now(),
		}, nil
	}

	// 随机选择文件
	selectedFiles := s.selectRandomFiles(files, s.config.Image.SelectCount)

	// 使用工作协程处理文件
	processedFiles, err := s.processFilesWithWorkers(ctx, selectedFiles)
	if err != nil {
		s.logger.Error("处理图片文件失败",
			zap.Error(err),
		)
		return nil, fmt.Errorf("处理图片文件失败: %w", err)
	}

	// 计算统计信息
	result := s.calculateResult(files, processedFiles, startTime)

	s.logger.Info("图片扫描完成",
		zap.Int("total_files", result.TotalFiles),
		zap.Int("processed_files", result.ProcessedFiles),
		zap.Int("added_files", result.AddedFiles),
		zap.Int("skipped_files", result.SkippedFiles),
		zap.Int("failed_files", result.FailedFiles),
		zap.Duration("duration", result.Duration),
	)

	return result, nil
}

// scanImageFiles 扫描目录获取所有图片文件
func (s *ImageScanServiceImpl) scanImageFiles(ctx context.Context) ([]string, error) {
	var files []string

	err := filepath.Walk(s.config.Image.RootPath, func(path string, info os.FileInfo, err error) error {
		select {
		case <-ctx.Done():
			return ctx.Err()
		default:
		}

		if err != nil {
			s.logger.Warn("访问文件失败",
				zap.String("path", path),
				zap.Error(err),
			)
			return nil // 继续处理其他文件
		}

		// 跳过目录
		if info.IsDir() {
			return nil
		}

		// 检查是否是图片文件
		if s.isImageFile(info.Name()) {
			// 只使用文件名（不包含路径）
			filename := filepath.Base(path)
			files = append(files, filename)
		}

		// 限制最大文件数
		if len(files) >= s.config.Image.MaxFiles {
			return filepath.SkipDir
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return files, nil
}

// isImageFile 检查是否为图片文件
func (s *ImageScanServiceImpl) isImageFile(filename string) bool {
	ext := strings.ToLower(filepath.Ext(filename))
	for _, validExt := range s.config.Image.FileExtensions {
		if ext == strings.ToLower(validExt) {
			return true
		}
	}
	return false
}

// selectRandomFiles 随机选择文件
func (s *ImageScanServiceImpl) selectRandomFiles(files []string, count int) []string {
	if len(files) <= count {
		return files
	}

	// 使用Fisher-Yates洗牌算法
	rand.Seed(time.Now().UnixNano())
	result := make([]string, len(files))
	copy(result, files)

	for i := len(result) - 1; i > 0; i-- {
		j := rand.Intn(i + 1)
		result[i], result[j] = result[j], result[i]
	}

	return result[:count]
}

// processFilesWithWorkers 使用工作协程处理文件
func (s *ImageScanServiceImpl) processFilesWithWorkers(ctx context.Context, files []string) ([]string, error) {
	if len(files) == 0 {
		return []string{}, nil
	}

	workers := s.config.Image.Workers
	if workers <= 0 {
		workers = 3
	}

	// 创建channel进行任务分发
	jobs := make(chan string, len(files))
	results := make(chan string, len(files))
	errors := make(chan error, len(files))

	var wg sync.WaitGroup

	// 启动工作协程
	for i := 0; i < workers; i++ {
		wg.Add(1)
		go s.worker(ctx, i, jobs, results, errors, &wg)
	}

	// 发送任务
	for _, file := range files {
		select {
		case jobs <- file:
		case <-ctx.Done():
			close(jobs)
			return nil, ctx.Err()
		}
	}
	close(jobs)

	// 等待所有工作协程完成
	wg.Wait()
	close(results)
	close(errors)

	// 收集结果
	var processedFiles []string
	for filename := range results {
		processedFiles = append(processedFiles, filename)
	}

	// 收集错误
	var errorList []string
	for err := range errors {
		if err != nil {
			errorList = append(errorList, err.Error())
		}
	}

	return processedFiles, nil
}

// worker 工作协程
func (s *ImageScanServiceImpl) worker(ctx context.Context, id int, jobs <-chan string, results chan<- string, errors chan<- error, wg *sync.WaitGroup) {
	defer wg.Done()

	for {
		select {
		case <-ctx.Done():
			return
		case filename, ok := <-jobs:
			if !ok {
				return
			}

			// 处理文件
			if err := s.processFile(ctx, filename); err != nil {
				s.logger.Error("处理图片文件失败",
					zap.Int("worker_id", id),
					zap.String("filename", filename),
					zap.Error(err),
				)
				errors <- err
				continue
			}

			results <- filename
		}
	}
}

// processFile 处理单个文件
func (s *ImageScanServiceImpl) processFile(ctx context.Context, filename string) error {
	// 检查文件是否存在
	filePath := filepath.Join(s.config.Image.RootPath, filename)
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		return fmt.Errorf("文件不存在: %s", filename)
	}

	// 添加到Redis，忽略所有插入错误
	_ = s.redisClient.AddVideo(ctx, s.config.Image.RedisKey, filename)

	return nil
}

// calculateResult 计算扫描结果
func (s *ImageScanServiceImpl) calculateResult(allFiles, processedFiles []string, startTime time.Time) *dto.ScanResult {
	endTime := time.Now()

	// 获取实际添加的文件数
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	actualCount, _ := s.redisClient.GetVideoCount(ctx, s.config.Image.RedisKey)

	// 计算各个统计值
	totalFiles := len(allFiles)
	processedCount := len(processedFiles)
	skippedCount := totalFiles - processedCount
	failedCount := 0 // 由于没有错误跟踪，假设没有失败

	return &dto.ScanResult{
		TotalFiles:     totalFiles,
		ProcessedFiles: processedCount,
		AddedFiles:     int(actualCount),
		SkippedFiles:   skippedCount,
		FailedFiles:    failedCount,
		Duration:       endTime.Sub(startTime),
		StartTime:      startTime,
		EndTime:        endTime,
	}
}

// GetScanStats 获取扫描统计信息
func (s *ImageScanServiceImpl) GetScanStats(ctx context.Context) (*ImageScanStats, error) {
	// 这里可以实现更详细的统计信息
	// 目前返回基础信息
	return &ImageScanStats{
		LastScanTime: time.Now(),
	}, nil
}

// CancelScan 取消正在进行的扫描
func (s *ImageScanServiceImpl) CancelScan() {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.cancelFunc != nil {
		s.cancelFunc()
		s.logger.Info("图片扫描任务已取消")
	}
}

// ScanImageFiles 扫描图片文件
func (s *ImageScanServiceImpl) ScanImageFiles(ctx context.Context) error {
	s.mu.Lock()
	if s.isScanning {
		s.mu.Unlock()
		return fmt.Errorf("图片扫描任务已在进行中")
	}
	s.isScanning = true
	s.mu.Unlock()

	defer func() {
		s.mu.Lock()
		s.isScanning = false
		s.mu.Unlock()
	}()

	// 创建可取消的上下文
	ctx, cancel := context.WithCancel(ctx)
	s.cancelFunc = cancel
	defer cancel()

	startTime := time.Now()
	s.logger.Info("开始扫描图片文件", zap.String("directory", s.config.Image.RootPath))

	// 扫描文件
	files, err := s.scanImageFiles(ctx)
	if err != nil {
		s.logger.Error("扫描图片文件失败", zap.Error(err), zap.String("directory", s.config.Image.RootPath))
		return fmt.Errorf("扫描图片文件失败: %w", err)
	}

	if len(files) == 0 {
		s.logger.Warn("没有找到图片文件", zap.String("directory", s.config.Image.RootPath))
		return nil
	}

	// 使用工作协程处理文件
	_, err = s.processFilesWithWorkers(ctx, files)
	if err != nil {
		s.logger.Error("处理图片文件失败", zap.Error(err))
		return fmt.Errorf("处理图片文件失败: %w", err)
	}

	duration := time.Since(startTime)
	s.logger.Info("图片文件扫描完成",
		zap.Int("total_files", len(files)),
		zap.Duration("duration", duration),
	)

	return nil
}

// GetTaskStatus 获取任务状态
func (s *ImageScanServiceImpl) GetTaskStatus(ctx context.Context) (*dto.TaskStatus, error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	status := &dto.TaskStatus{
		TaskID:    "image_scan_task",
		StartTime: time.Now(),
	}

	if s.isScanning {
		status.Status = "running"
	} else {
		status.Status = "completed"
	}

	return status, nil
}

// ValidateDirectory 验证目录是否存在且可读
func (s *ImageScanServiceImpl) ValidateDirectory() error {
	info, err := os.Stat(s.config.Image.RootPath)
	if err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("目录不存在: %s", s.config.Image.RootPath)
		}
		return fmt.Errorf("访问目录失败: %w", err)
	}

	if !info.IsDir() {
		return fmt.Errorf("路径不是目录: %s", s.config.Image.RootPath)
	}

	// 检查目录权限
	if _, err := os.Open(s.config.Image.RootPath); err != nil {
		return fmt.Errorf("无法读取目录: %w", err)
	}

	return nil
}

// GetDirectoryStats 获取目录统计信息
func (s *ImageScanServiceImpl) GetDirectoryStats() (map[string]interface{}, error) {
	stats := make(map[string]interface{})

	// 获取目录信息
	info, err := os.Stat(s.config.Image.RootPath)
	if err != nil {
		return nil, err
	}

	stats["path"] = s.config.Image.RootPath
	stats["exists"] = true
	stats["is_dir"] = info.IsDir()
	stats["mod_time"] = info.ModTime()

	// 获取文件数量（简单统计）
	var fileCount int
	err = filepath.Walk(s.config.Image.RootPath, func(path string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() && s.isImageFile(info.Name()) {
			fileCount++
		}
		return nil
	})

	stats["image_file_count"] = fileCount
	return stats, nil
}
